package com.df.my_net_game.common.utils;

import com.baidu.bjf.remoting.protobuf.Codec;
import com.baidu.bjf.remoting.protobuf.ProtobufProxy;
import com.baidu.bjf.remoting.protobuf.annotation.ProtobufClass;
import com.df.my_net_game.game_rpc.EzGameMsg;
import com.df.my_net_game.game_rpc.service.annotation.LogicController;
import com.df.my_net_game.game_rpc.service.annotation.LogicMapping;
import com.df.my_net_game.game_rpc.types.ByteWrapper;
import com.df.my_net_game.game_rpc.types.IntWrapper;
import com.df.my_net_game.game_rpc.types.LongWrapper;
import com.df.my_net_game.game_rpc.types.ShortWrapper;
import com.df.my_net_game.game_rpc.utils.TypeUtils;
import com.esotericsoftware.reflectasm.MethodAccess;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Data
public class LogicMethodMgr {


    //TODO 换成数组映射
    //{controllerId:唯一controllerId对应的数据}
    private static ConcurrentHashMap<Integer, LogicControllerData> datas = new ConcurrentHashMap<Integer, LogicControllerData>();
    //===============================映射数据=============================================
    //路径id和处理方法的映射
    private static HashMap<Integer, LogicMethodData> methodsCache = new HashMap<>();

    //===============================映射数据=============================================

    public  static void setControllerBaseTimes(Integer controllerBaseTimes){
    }

    public static ConcurrentHashMap<Integer, LogicControllerData> getDatas(){
        return datas;
    }

    public static LogicMethodData getLogicMethodData(Integer methodFullId){
        LogicMethodData data = methodsCache.get(methodFullId);
        return data;
    }

    public static Object dispacherGrpcMsg(EzGameMsg msg){

        return null;
    }

    //TODO 编译的时候就直接解析？
    public static void parseControllerObj(Object ctlObj){
        if(ctlObj==null){
            return;
        }
        Class<?> ctlObjClass = ctlObj.getClass();
        LogicController annotation = ctlObjClass.getAnnotation(LogicController.class);
        if(annotation==null){
            return;
        }

        String name = annotation.name();
        String desc = annotation.desc();
        int ctlId = annotation.id();

        //{name:数据}
        LogicControllerData controllerData = datas.getOrDefault(ctlId,new LogicControllerData());
        datas.put(ctlId, controllerData);
        controllerData.setControllerName(name);
        controllerData.setControllerClassName(ctlObjClass.getName());
        controllerData.setControllerClassType(ctlObjClass);
        controllerData.setControllerId(ctlId);
        controllerData.setControllerDesc(desc);

        Method[] methods = ctlObjClass.getMethods();
        //消息种类：
        //1.客户端请求并返回
        //定位哪个类的哪个方法，用完成的路径计算的id进行映射
        //方法的参数使用参数类名计算的唯一id和类进行映射
        //方法的返回值使用类名计算的唯一id和类进行映射
        //2.客户端请求不返回
        //3.服务器给客户端推送
        MethodAccess methodAccess = MethodAccess.get(ctlObjClass);

        for (Method method : methods) {
            LogicMapping methodMapping = method.getAnnotation(LogicMapping.class);
            if(methodMapping==null){
                continue;
            }
            String methodName = method.getName();

            String fullPath = name +"/"+ methodName;
            //计算消息id
            int methodId = methodMapping.id();

            //参数
            int parameterCount = method.getParameterCount();
            if(parameterCount>1){
                log.error("消息处理方法参数超过1个！路径："+fullPath);
                continue;
            }
            //高低个占用16位
            Integer methodFullId = ctlId<<16+methodId&0xffff;
            LogicMethodData methodDataCache = methodsCache.get(methodFullId);
            if(methodDataCache!=null){
                throw new RuntimeException("logic method 重复，重复方法id："+methodFullId);
            }
            //TODO 父路由和子路由使用二维数组进行存储，访问时效率高，map获取时会进行hash运算
            LogicMethodData methodData = controllerData.getMethos().getOrDefault(methodFullId, new LogicMethodData());
            methodAccess.getIndex(method.getName(),method.getParameterTypes());
            methodData.setMethodId(methodId);
            methodData.setMethodFullId(methodFullId);
            methodData.setMethodName(methodName);
            methodData.setMethodDesc(methodMapping.desc());

            Class paramType = null;
            if(parameterCount==1){
                Parameter parameter = method.getParameters()[0];
                ProtobufClass paramAnno = parameter.getType().getAnnotation(ProtobufClass.class);
                if(paramAnno==null){
                    //判定是不是基本类型
                    if(!TypeUtils.isBaseType(parameter.getType())){
                        log.error("消息处理方法只能接收被Protobuf注解的消息或基本类型！路径："+fullPath);
                        continue;
                    }else {
                        methodData.setParamIsBaseType(true);
                    }
                }
                paramType = parameter.getType();
                methodData.setParamType(paramType);
                methodData.setParamName(parameter.getName());
            }


            //返回值
            Class returnType = method.getReturnType();
            Type genericReturnType = method.getGenericReturnType();
            if(returnType!=null){
                Annotation returnAnno = returnType.getAnnotation(ProtobufClass.class);
                if(returnAnno==null){
                    if(!TypeUtils.isBaseType(returnType)){
                        log.error("消息处理方法返回类型必须被Protobuf注解！路径："+fullPath);
                    }else {
                        methodData.setReturnIsBaseType(true);
                    }
                    continue;
                }
                methodData.setReturnType(returnType);
            }


            //id和路径映射



            int methodIndex = methodAccess.getIndex(method.getName());

            methodData.methodAccess = methodAccess;
            methodData.methodIndex = methodIndex;
            methodData.instance = ctlObj;


            if(paramType!=null){
                if(methodData.isParamIsBaseType()){
                    //TODO 包装类型的编码器
                    Class wrapperType = createWrapperTypeCodec(paramType);
                    if(wrapperType==null){
                        throw new RuntimeException("未支持的基本类型："+paramType.getName());
                    }
                    Codec codec = ProtobufProxy.create(wrapperType);
                    methodData.setParamWrapperType(wrapperType);
                }else {
                    Codec codec = ProtobufProxy.create(paramType);
                    System.out.println(1);
                }
            }
            if(returnType!=null){
                if(methodData.isReturnIsBaseType()){
                    //TODO 包装类型的编码器
                    Class wrapperType = createWrapperTypeCodec(paramType);
                    if(wrapperType==null){
                        throw new RuntimeException("未支持的基本类型："+paramType.getName());
                    }
                    Codec codec = ProtobufProxy.create(wrapperType);
                    methodData.setReturnWrapperType(wrapperType);
                }else {
                    Codec codec = ProtobufProxy.create(returnType);
                }
            }
            controllerData.getMethos().put(methodFullId,methodData);
            methodsCache.put(methodFullId,methodData);
        }

    }



    private static Class createWrapperTypeCodec(Class paramType) {
        // byte short int long list map
        if(paramType.equals(Byte.TYPE)){
            return ByteWrapper.class;
        }
        if(paramType.equals(Short.TYPE)){
            return ShortWrapper.class;
        }
        if(paramType.equals(Integer.TYPE)){
            return IntWrapper.class;
        }
        if(paramType.equals(Long.TYPE)){
            return LongWrapper.class;
        }
//        if(paramType.equals(Byte.TYPE)){
//            return ByteWrapper.class;
//        }
//        if(paramType.equals(Byte.TYPE)){
//            return ByteWrapper.class;
//        }
        throw new RuntimeException("未支持的基本类型："+paramType.getName());
    }

    private static int getmsgId(String fullPath, Set<Integer> integers) {
        char[] charArray = fullPath.toCharArray();
        int h = 0;
        for (char c :charArray){
            h = 31 * h + c;
        }
        h = h & 0x0000ffff;
        while (integers.contains(h)) {
            h++;
            h = h & 0x0000ffff;
        }
        return h;
    }

    public static void main(String[] args) {
        String str = "special_item/C2G_UsesCapacityBall_SpecialItem";
        int dengfan = getmsgId(str, new HashSet<Integer>());
        System.out.println(dengfan);
        System.out.println(str.hashCode());
    }
}
